﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace GN.Pay.Client
{
    /// <summary>
    /// 类型帮助
    /// </summary>
    public static class TypeUtils
    {
        /// <summary>
        /// 获取类型默认构造的分配的类型集合
        /// </summary>
        /// <param name="assembly">程序集</param> 
        /// <param name="isReferencedAssemblie">是否引用程序集</param>      
        /// <returns></returns>
        public static List<Type> GetDefaultConstructorAssignableTypes<T>(this Assembly assembly,
            Type attributeType, bool isReferencedAssemblie)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }
            Func<Type, bool> predicate;
            if (attributeType != null)
            {
                predicate = t => !t.IsAbstract && !t.IsInterface && t.GetConstructor(Type.EmptyTypes) != null && t.IsDefined(attributeType, false);
            }
            else
            {
                predicate = t => !t.IsAbstract && !t.IsInterface && t.GetConstructor(Type.EmptyTypes) != null;
            }
            return GetAssignableTypes<T>(assembly, predicate, isReferencedAssemblie);
        }

        /// <summary>
        /// 获取分配类型类型集合
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <typeparam name="TAttribute">特性类型</typeparam>
        /// <param name="assembly">程序集</param>
        /// <param name="predicate">条件</param>
        /// <param name="isReferencedAssemblie">是否包含引用</param>
        /// <returns></returns>
        public static Dictionary<Type, TAttribute> GetAssignableTypeDictionary<T, TAttribute>(this Assembly assembly, Func<Type, bool> predicate, bool isReferencedAssemblie) where TAttribute : Attribute
        {
            Func<Type, bool> predicate2;
            if (predicate != null)
            {
                predicate2 = t => predicate(t) && t.IsDefined(typeof(TAttribute), false);
            }
            else
            {
                predicate2 = t => t.IsDefined(typeof(TAttribute), false);
            }
            var typeList = GetAssignableTypes<T>(assembly, predicate2, isReferencedAssemblie);
            Dictionary<Type, TAttribute> dic = new Dictionary<Type, TAttribute>();
            foreach (var type in typeList)
            {
                dic[type] = Attribute.GetCustomAttribute(type, typeof(TAttribute)) as TAttribute;
            }
            return dic;
        }

        /// <summary>
        /// 获取分配类型类型集合
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="assembly">程序集</param>
        /// <param name="predicate">条件</param>
        /// <param name="isReferencedAssemblie">是否包含引用</param>
        /// <returns></returns>
        public static List<Type> GetAssignableTypes<T>(this Assembly assembly, Func<Type, bool> predicate, bool isReferencedAssemblie)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }
            Type convertType = typeof(T);
            Func<Type, bool> predicate2;
            if (predicate != null)
            {
                predicate2 = t => predicate(t) && convertType.IsAssignableFrom(t);
            }
            else
            {
                predicate2 = t => convertType.IsAssignableFrom(t);
            }
            Type[] types = assembly.GetTypes(predicate2);
            var typeList = new List<Type>(types);
            if (isReferencedAssemblie)
            {
                var assemblies = assembly.GetReferencedAssemblies();
                foreach (var assemblie in assemblies)
                {
                    assembly = Assembly.Load(assemblie);
                    typeList.AddRange(assembly.GetTypes(predicate2));
                }
            }
            return typeList;
        }

        private static Type[] AssemblyTypes(Assembly assembly)
        {
            try
            {
                Type[] types = assembly.GetTypes();
                return types;
            }
            catch (ReflectionTypeLoadException err)
            {
                if (err.LoaderExceptions.Length > 0)
                {
                    throw err.LoaderExceptions[0];
                }
                throw;
            }
        }

        /// <summary>
        /// 获取类型集合
        /// </summary>
        /// <param name="assembly">程序集</param>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        public static Type[] GetTypes(this Assembly assembly, Func<Type, bool> predicate)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }
            Type[] types = AssemblyTypes(assembly);
            if (predicate != null)
            {
                return types.Where(predicate).ToArray();
            }
            return types;
        }
    }
}
